#include "SimpleProxyModel.h"



#include <ItemModel.h>



#include <QRegExp>
#include <QDebug>
#include <QTimer>



SimpleProxyModel::SimpleProxyModel(QObject *parent) :
    QAbstractProxyModel(parent)
{
}



QModelIndex SimpleProxyModel::index(int row, int column, const QModelIndex &proxyParent) const
{
    if (!hasIndex(row, column, proxyParent) || proxyParent.isValid()) return QModelIndex();

    QMapIterator<SourceIndex, ProxyIndex> it(m_indexMap);

    while (it.hasNext())
    {
        it.next();
        if (it.value().row() != row ||
            it.value().column() != column) continue;

        return it.value();
    }

    return QModelIndex();
}



QModelIndex SimpleProxyModel::parent(const QModelIndex &proxyChild) const
{
    Q_UNUSED(proxyChild);

    return QModelIndex();
}



int SimpleProxyModel::rowCount(const QModelIndex &proxyParent) const
{
    if (proxyParent.isValid()) return 0;

    int rows = 0;
    QMapIterator<SourceIndex, ProxyIndex> it(m_indexMap);

    int row = -1;
    while (it.hasNext())
    {
        it.next();
        if (it.value().row() > row) rows++;
        row = it.value().row();
    }

    return rows;
}



int SimpleProxyModel::columnCount(const QModelIndex &proxyParent) const
{
    Q_UNUSED(proxyParent);
    return m_fields.count();
}



QModelIndex SimpleProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
{
    return m_indexMap.value(sourceIndex);
}



QModelIndex SimpleProxyModel::mapToSource(const QModelIndex &proxyIndex) const
{
    return m_indexMap.key(proxyIndex);
}



QVariant SimpleProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role != Qt::DisplayRole || !m_fields.contains(section)) return QVariant();

    QString fieldName = m_fields.value(section);
    if (fieldName.isEmpty()) return QAbstractProxyModel::headerData(section, orientation, role);

    return fieldName;
}



ItemModel* SimpleProxyModel::itemModel() const
{
    return dynamic_cast<ItemModel*>(sourceModel());
}



void SimpleProxyModel::setItemModel(ItemModel *model)
{
    disconnect(this, SLOT(onRowsInserted(QModelIndex,int,int)));
    disconnect(this, SLOT(onRowsRemoved(QModelIndex,int,int)));
    if (sourceModel()) disconnect(sourceModel(), SIGNAL(layoutChanged()), this, SLOT(remap()));

    setSourceModel(model);

    if (!model) return;

    connect(model, SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(onRowsInserted(QModelIndex,int,int)));
    connect(model, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(onRowsRemoved(QModelIndex,int,int)));
    connect(model, SIGNAL(layoutChanged()), this, SLOT(remap()));
}



void SimpleProxyModel::addExcludeFilter(const QString &idPattern, const int &filter)
{
    if (m_inclusions.contains(idPattern) && m_inclusions.value(idPattern) == filter)
    {
        m_inclusions.remove(idPattern);
    }

    m_exclusions.insert(idPattern, filter);
}



void SimpleProxyModel::addIncludeFilter(const QString &idPattern, const int &filter)
{
    if (m_exclusions.contains(idPattern) && m_exclusions.value(idPattern) == filter)
    {
        m_exclusions.remove(idPattern);
    }

    m_inclusions.insert(idPattern, filter);
}



int SimpleProxyModel::excludeFilter(const QString& itemId) const
{
    int ret = NoFilter;
    foreach (const QString idPattern, m_exclusions.keys())
    {
        QRegExp regExp(idPattern);

        if (regExp.indexIn(itemId) == -1) continue;

        int filter = m_exclusions.value(idPattern);

        switch (filter)
        {
        case NoFilter:
            continue;

        case RegExpFilter:
            break;

        case ContainsFilter:
            if (itemId.contains(idPattern)) break;
            continue;

        case StartsWithFilter:
            if (itemId.startsWith(idPattern)) break;
            continue;

        case EndsWithFilter:
            if (itemId.endsWith(idPattern)) break;
            continue;

        default:
            continue;
        }

        if (ret < filter) ret = filter;
    }

    return ret;
}



int SimpleProxyModel::includeFilter(const QString& itemId) const
{
    int ret = NoFilter;
    foreach (const QString idPattern, m_inclusions.keys())
    {
        QRegExp regExp(idPattern);

        if (regExp.indexIn(itemId) == -1) continue;

        int filter = m_inclusions.value(idPattern);

        switch (filter)
        {
        case NoFilter:
            continue;

        case RegExpFilter:
            break;

        case ContainsFilter:
            if (!itemId.contains(idPattern)) continue;
            break;

        case StartsWithFilter:
            if (!itemId.startsWith(idPattern)) continue;
            break;

        case EndsWithFilter:
            if (!itemId.endsWith(idPattern)) continue;
            break;

        default:
            continue;
        }

        if (ret < filter) ret = filter;
    }

    return ret;
}



void SimpleProxyModel::addField(const int &sourceField, const QString &fieldName)
{
    if (m_fields.contains(sourceField)) return;

    m_fields.insert(sourceField, fieldName);
}



QAbstractItemModel* SimpleProxyModel::sourceModel() const
{
    return QAbstractProxyModel::sourceModel();
}



void SimpleProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
{
    QAbstractProxyModel::setSourceModel(sourceModel);
    QTimer::singleShot(0, this, SLOT(remap()));
}



void SimpleProxyModel::onRowsInserted(const QModelIndex& sourceParent, int start, int end)
{
    Q_UNUSED(end);

    emit layoutAboutToBeChanged();
    mapChildren(sourceParent, start);
    emit layoutChanged();
}



void SimpleProxyModel::onRowsRemoved(const QModelIndex& sourceParent, int start, int end)
{
    Q_UNUSED(sourceParent);
    Q_UNUSED(start);
    Q_UNUSED(end);

    // hopefully there won't be much deleting...
    remap();
}



void SimpleProxyModel::remap()
{
    // reset model
    beginResetModel();
    m_indexMap.clear();
    endResetModel();

    // create mapping
    emit layoutAboutToBeChanged();
    mapChildren();
    emit layoutChanged();
}



void SimpleProxyModel::mapChildren(const QModelIndex &sourceParent, int start)
{
    ItemModel* model = itemModel();
    if (!model) return;

    QModelIndex sourceIndex = model->index(start, AbstractItem::IdData, sourceParent);

    while (sourceIndex.isValid())
    {
        QString itemId = sourceIndex.data(Qt::EditRole).toString();

        int include = includeFilter(itemId);
        int exclude = excludeFilter(itemId);

        if (include > exclude)
        {
            mapIndex(sourceIndex);
            //qDebug() << Q_FUNC_INFO << itemId << include << exclude;
        }

        mapChildren(sourceIndex);
        sourceIndex = sourceIndex.sibling(sourceIndex.row()+1, sourceIndex.column());
    }


}



void SimpleProxyModel::mapIndex(const QModelIndex &sourceIndex)
{
    int row = rowCount(QModelIndex());
    foreach (const int field, m_fields.keys())
    {
        QModelIndex fieldIndex = sourceIndex.sibling(sourceIndex.row(), field);
        if (!fieldIndex.isValid()) continue;

        //qDebug() << Q_FUNC_INFO << "Mapped source index " << fieldIndex.row() << fieldIndex.column();
        //qDebug() << Q_FUNC_INFO << "Created proxy index to" << m_indexMap.count() << m_fields.keys().indexOf(field) << m_fields.value(field);

        m_indexMap.insert(fieldIndex, createIndex(row, m_fields.keys().indexOf(field), fieldIndex.internalPointer()));
    }
}


